﻿using HalconDotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 基于灰度的裂纹长度测量系统
{
    public class HalconCrackMeasure
    {
        /// <summary>
        /// 裂纹
        /// </summary>
        HObject ho_Image;
        /// <summary>
        /// 预制裂纹区域ROI图
        /// </summary>
        HObject ho_ROI_1;
        /// <summary>
        /// 预制裂纹区域裁剪后去除黑色区域图
        /// </summary>
        HObject ho_CrackImage1;
        /// <summary>
        /// 预制裂纹区域裁剪图
        /// </summary>
        HObject ho_CrackImageOriginal;
        /// <summary>
        /// 二值化裁剪图
        /// </summary>
        HObject ho_ImageReduced;
        /// <summary>
        /// 起点图
        /// </summary>
        HObject ho_CrossHarris;
        /// <summary>
        /// 终点图
        /// </summary>
        HObject ho_CrossHarris1;
        /// <summary>
        /// 裂纹区域ROI图
        /// </summary>
        HObject ho_ROI_2;
        /// <summary>
        /// 二值化图
        /// </summary>
        HObject ho_Regions;
        /// <summary>
        /// 自适应阈值分割图
        /// </summary>
        HObject ho_Region1;
        /// <summary>
        /// 开操作图
        /// </summary>
        HObject ho_RegionOpening;
        /// <summary>
        /// 连通域图
        /// </summary>
        HObject ho_ConnectedRegions;
        /// <summary>
        /// 裂纹区域裁剪图
        /// </summary>
        HObject ho_CrackImage2;
        /// <summary>
        /// 裂纹选择图
        /// </summary>
        HObject ho_SelectedRegions;
        /// <summary>
        /// 裂纹单像素骨架图
        /// </summary>
        HObject ho_Skeleton;

        /// <summary>
        /// 模板数字代号
        /// </summary>
        HTuple hv_ModelID = null;
        /// <summary>
        /// 角点检测列数组
        /// </summary>
        HTuple hv_ColHarris = null;
        /// <summary>
        /// 角点检测行平均值
        /// </summary>
        HTuple hv_Mean = null;
        /// <summary>
        /// 角点检测行数组
        /// </summary>
        HTuple hv_RowHarris = null;
        /// <summary>
        /// 裂纹区域行1
        /// </summary>
        HTuple hv_Row11 = null;
        /// <summary>
        /// 裂纹区域列1
        /// </summary>
        HTuple hv_Column11 = null;
        /// <summary>
        /// 裂纹区域列2
        /// </summary>
        HTuple hv_Column22 = null;
        /// <summary>
        /// 自适应阈值分割输出参数
        /// </summary>
        HTuple hv_UsedThreshold = null;
        /// <summary>
        /// 单像素骨架起点列数组
        /// </summary>
        HTuple hv_BeginCol = null;
        /// <summary>
        /// 角点检测列最大值
        /// </summary>
        HTuple hv_Max = null;
        /// <summary>
        /// 裂纹区域行2
        /// </summary>
        HTuple hv_Row22 = null;
        /// <summary>
        /// 单像素骨架终点行数组
        /// </summary>
        HTuple hv_EndRow = null;
        /// <summary>
        /// 模板picturebox句柄
        /// </summary>
        HTuple ModelWindowHandle;
        /// <summary>
        /// 单像素骨架起点行数组
        /// </summary>
        HTuple hv_BeginRow = null;
        /// <summary>
        /// 单像素骨架终点列数组
        /// </summary>
        HTuple hv_EndCol = null;
        /// <summary>
        /// 裂纹图像picturebox句柄
        /// </summary>
        HTuple MainWindowHandle;

        /// <summary>
        /// 读图
        /// </summary>
        /// <param name="path"></param>
        /// <param name="pictureBox"></param>
        public void ReadImage(string path, PictureBox pictureBox)
        {
            //传入句柄
            HTuple FatherWindow = pictureBox.Handle;
            //读图
            HOperatorSet.ReadImage(out ho_Image, path);
            //防止更改第一次句柄
            if (MainWindowHandle == null)
            {
                //打开句柄窗口
                HOperatorSet.OpenWindow(0, 0, pictureBox.Width, pictureBox.Height, FatherWindow, "visible", "", out MainWindowHandle);
            }
            //不减掉超出图像大小范围的区域
            HSystem.SetSystem("clip_region", "false");
            //获取裂纹图像参数
            HOperatorSet.GetImagePointer1(ho_Image, out _, out _, out HTuple Width, out HTuple Height);
            //句柄窗口自适应图片大小
            HOperatorSet.SetPart(MainWindowHandle, 0, 0, Height - 1, Width - 1);
            //清空句柄窗口
            HOperatorSet.ClearWindow(MainWindowHandle);
            //显示裂纹图像
            HOperatorSet.DispObj(ho_Image, MainWindowHandle);
        }

        /// <summary>
        /// 创建模板
        /// </summary>
        /// <param name="b">true创建；false读取</param>
        /// <param name="pictureBox">显示模板picturebox</param>
        /// <param name="path">读取路径，不读取则传入空字符串即可</param>
        public void CreateModel(bool b, PictureBox pictureBox, string path)
        {
            //清空句柄窗口重新显示裂纹图像，防止二次建模版有上次痕迹
            HOperatorSet.ClearWindow(MainWindowHandle);
            HOperatorSet.DispObj(ho_Image, MainWindowHandle);
            //传入句柄
            HTuple FatherWindow = pictureBox.Handle;
            //防止更改第一次句柄
            if (ModelWindowHandle == null)
            {
                //打开句柄窗口
                HOperatorSet.OpenWindow(0, 0, pictureBox.Width, pictureBox.Height, FatherWindow, "visible", "", out ModelWindowHandle);
            }
            //不减掉超出图像大小范围的区域
            HSystem.SetSystem("clip_region", "false");
            if (b)
            {
                //ROI参数申明
                HTuple hv_Row1, hv_Column1, hv_Row2, hv_Column2;
                //红色画ROI
                HOperatorSet.SetColor(MainWindowHandle, "red");
                //画矩形
                HOperatorSet.DrawRectangle1(MainWindowHandle, out hv_Row1, out hv_Column1, out hv_Row2, out hv_Column2);
                //获得ROI
                HOperatorSet.GenRectangle1(out ho_ROI_1, hv_Row1, hv_Column1, hv_Row2, hv_Column2);
                //裁剪ROI
                HOperatorSet.ReduceDomain(ho_Image, ho_ROI_1, out ho_CrackImageOriginal);
                //去除无关区域
                HOperatorSet.CropDomain(ho_CrackImageOriginal, out ho_CrackImage1);
            }
            else
            {
                //读图
                HOperatorSet.ReadImage(out HObject Image1, path);
                //转灰度图
                HOperatorSet.Rgb1ToGray(Image1, out ho_CrackImage1);
            }
            //创建模板
            HOperatorSet.CreateNccModel(ho_CrackImage1, "auto", 0, 0, "auto", "use_polarity", out hv_ModelID);
            //获得模板参数
            HOperatorSet.GetImagePointer1(ho_CrackImage1, out _, out _, out HTuple Width, out HTuple Height);
            //自适应大小显示模板
            HOperatorSet.SetPart(ModelWindowHandle, 0, 0, Height - 1, Width - 1);
            //清空句柄窗口
            HOperatorSet.ClearWindow(ModelWindowHandle);
            //显示模板图像
            HOperatorSet.DispObj(ho_CrackImage1, ModelWindowHandle);
        }

        /// <summary>
        /// 模板匹配
        /// </summary>
        public void ModelMatch()
        {
            //模板匹配参数申明
            HTuple hv_Column, hv_Angle, hv_Score, hv_Row;
            HOperatorSet.FindNccModel(ho_Image, hv_ModelID, 0, 0, 0.5, 1, 0.5, "true", 0, out hv_Row, out hv_Column, out hv_Angle, out hv_Score);
            //绿色画图
            HOperatorSet.SetColor(MainWindowHandle, "green");
            //只画边框
            HOperatorSet.SetDraw(MainWindowHandle, "margin");
            //获得模板参数
            HOperatorSet.GetImagePointer1(ho_CrackImage1, out _, out _, out HTuple Width, out HTuple Height);
            //清空窗口
            HOperatorSet.ClearWindow(MainWindowHandle);
            //显示裂纹图像
            HOperatorSet.DispObj(ho_Image, MainWindowHandle);
            //显示匹配结果
            HOperatorSet.DispRectangle2(MainWindowHandle, hv_Row, hv_Column, hv_Angle, Width / 2, Height / 2);
        }

        /// <summary>
        /// 裂纹检测
        /// </summary>
        public float FindCrack()
        {
            //二值化
            HOperatorSet.Threshold(ho_CrackImageOriginal, out ho_Regions, 36, 189);
            //裁剪
            HOperatorSet.ReduceDomain(ho_CrackImageOriginal, ho_Regions, out ho_ImageReduced);
            //角点检测
            HOperatorSet.PointsHarris(ho_ImageReduced, 0.7, 2, 0.08, 1000, out hv_RowHarris, out hv_ColHarris);
            //显示起点
            HOperatorSet.TupleMean(hv_RowHarris, out hv_Mean);
            HOperatorSet.TupleMax(hv_ColHarris, out hv_Max);
            HOperatorSet.ClearWindow(MainWindowHandle);
            HOperatorSet.DispObj(ho_Image, MainWindowHandle);
            HOperatorSet.SetColor(MainWindowHandle, "green");
            HOperatorSet.GenCrossContourXld(out ho_CrossHarris, hv_Mean, hv_Max, 20, 45);
            HOperatorSet.DispCross(MainWindowHandle, hv_Mean, hv_Max, 20, 45);
            //阈值分割
            HOperatorSet.DrawRectangle1(MainWindowHandle, out hv_Row11, out hv_Column11, out hv_Row22, out hv_Column22);
            //选择ROI
            HOperatorSet.GenRectangle1(out ho_ROI_2, hv_Row11, hv_Column11, hv_Row22, hv_Column22);
            HOperatorSet.ReduceDomain(ho_Image, ho_ROI_2, out ho_CrackImage2);
            //自适应阈值分割
            HOperatorSet.BinaryThreshold(ho_CrackImage2, out ho_Region1, "max_separability", "dark", out hv_UsedThreshold);
            //开操作
            HOperatorSet.OpeningRectangle1(ho_Region1, out ho_RegionOpening, 15, 1);
            //连通域
            HOperatorSet.Connection(ho_RegionOpening, out ho_ConnectedRegions);
            //裂纹选择
            HOperatorSet.SelectShape(ho_ConnectedRegions, out ho_SelectedRegions, "area", "and", 1210.72, 9602.59);
            //提取裂纹骨架并显示
            HOperatorSet.Skeleton(ho_SelectedRegions, out ho_Skeleton);
            HOperatorSet.SetColor(MainWindowHandle, "red");
            HOperatorSet.DispRegion(ho_Skeleton, MainWindowHandle);
            //显示终点
            HOperatorSet.SetColor(MainWindowHandle, "green");
            HOperatorSet.SplitSkeletonLines(ho_Skeleton, 3, out hv_BeginRow, out hv_BeginCol, out hv_EndRow, out hv_EndCol);
            HOperatorSet.GenCrossContourXld(out ho_CrossHarris1, hv_BeginRow.TupleSelect(0), hv_BeginCol.TupleSelect(0), 20, 45);
            HOperatorSet.DispCross(MainWindowHandle, hv_BeginRow.TupleSelect(0), hv_BeginCol.TupleSelect(0), 20, 45);
            //计算长度
            float end = hv_BeginCol.TupleSelect(0);
            float start = float.Parse(hv_Max.ToString());
            float length = (end - start) * 0.01135f;
            return length;
        }
    }
}
